#include "StdAfx.hpp"
#include "Intro.hpp"
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <d3dx9.h>
#include <windows.h>
#include <cassert>
#include <cstdlib>

namespace {

const float IMAGE_SIZE_PERCENT = 0.1f;
const float DURATION = 1.f;

const char * const SHADER_CODE =
	"sampler g_Sampler : s0; "
	"float4 g_Color : c0; "
	"void PS(in float2 texCoord : TEXCOORD0, out float4 Out : COLOR0) { "
	"	Out = tex2D(g_Sampler, texCoord) * g_Color; "
	"} ";

const D3DVERTEXELEMENT9 VERTEX_DECL[] = {
	{ 0,  0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0 },
	{ 0, 16, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD,  0 },
	{0xFF,0,D3DDECLTYPE_UNUSED, 0,0,0} // Why D3DDECL_END is undeclared?!
};

struct VERTEX
{
	D3DXVECTOR4 Pos;
	D3DXVECTOR2 TexCoord;

	VERTEX() { }
	VERTEX(const D3DXVECTOR4 &pos, const D3DXVECTOR2 &texCoord) : Pos(pos), TexCoord(texCoord) { }
};

} // namespace


////////////////////////////////////////////////////////////////////////////////
// class Intro_pimpl

class Intro_pimpl
{
public:
	Intro_pimpl(IDirect3DDevice9 *d3dDev, const char *textureFileName, unsigned screenSizeX, unsigned screenSizeY);
	~Intro_pimpl();
	void Render();

private:
	IDirect3DDevice9 *m_Dev;
	IDirect3DTexture9 *m_Texture;
	IDirect3DPixelShader9 *m_PixelShader;
	IDirect3DVertexDeclaration9 *m_VertexDecl;
	DWORD m_StartTime;
	D3DXVECTOR2 m_ScreenSize;
	float m_ImgHalfsize;
};

Intro_pimpl::Intro_pimpl( IDirect3DDevice9 *d3dDev, const char *textureFileName, unsigned screenSizeX, unsigned screenSizeY )
: m_Dev(d3dDev)
, m_Texture(NULL)
, m_PixelShader(NULL)
, m_VertexDecl(NULL)
, m_StartTime(GetTickCount())
, m_ScreenSize((float)screenSizeX, (float)screenSizeY)
, m_ImgHalfsize(screenSizeY * IMAGE_SIZE_PERCENT)
{
	HRESULT hr = D3DXCreateTextureFromFileEx(m_Dev, textureFileName, D3DX_DEFAULT, D3DX_DEFAULT, 1, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &m_Texture);
	
	if (FAILED(hr))
	{
		assert(0);
		m_Texture = NULL;
	}

	ID3DXBuffer *shaderBuf = NULL;
	hr = D3DXCompileShader(SHADER_CODE, strlen(SHADER_CODE), NULL, NULL, "PS", "ps_3_0", 0, &shaderBuf, NULL, NULL);

	if (SUCCEEDED(hr))
	{
		hr = m_Dev->CreatePixelShader((const DWORD*)shaderBuf->GetBufferPointer(), &m_PixelShader);
		if (FAILED(hr))
		{
			assert(0);
			m_PixelShader = NULL;
		}
	}
	else
		assert(0);

	if (shaderBuf)
		shaderBuf->Release();

	hr = m_Dev->CreateVertexDeclaration(VERTEX_DECL, &m_VertexDecl);
	if (FAILED(hr))
	{
		assert(0);
		m_VertexDecl = NULL;
	}
}

Intro_pimpl::~Intro_pimpl()
{
	if (m_VertexDecl)
		m_VertexDecl->Release();
	if (m_PixelShader)
		m_PixelShader->Release();
	if (m_Texture)
		m_Texture->Release();
}

void Intro_pimpl::Render()
{
	float t = (GetTickCount() - m_StartTime) * 0.001f;
	float progress = t / DURATION;
	if (progress > 1.f)
		progress = 1.f;

	m_Dev->Clear(0, NULL, D3DCLEAR_TARGET, 0, 1.f, 0);

	m_Dev->SetRenderState(D3DRS_ZENABLE, FALSE);
	m_Dev->SetRenderState(D3DRS_STENCILENABLE, FALSE);
	m_Dev->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
	m_Dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	m_Dev->SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_SRCALPHA);
	m_Dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

	m_Dev->SetPixelShader(m_PixelShader);
	m_Dev->SetVertexDeclaration(m_VertexDecl);

	m_Dev->SetTexture(0, m_Texture);
	m_Dev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
	m_Dev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
	m_Dev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	m_Dev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	m_Dev->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);

	D3DXVECTOR2 dstPos = m_ScreenSize * 0.5f;
	D3DXVECTOR2 srcPos[4] = {
		D3DXVECTOR2(m_ScreenSize.x*0.5f, -m_ImgHalfsize),
		D3DXVECTOR2(               - m_ImgHalfsize, m_ScreenSize.y + m_ImgHalfsize),
		D3DXVECTOR2(m_ScreenSize.x + m_ImgHalfsize, m_ScreenSize.y + m_ImgHalfsize),
		dstPos,
	};

	float flashProgress = 1.f;
	if (progress >= 0.8f)
		flashProgress = (progress - 0.8f) * 5.f;

	float sizeFactor;
	D3DXVECTOR2 pos;
	D3DXVECTOR4 color;
	for (uint i = 0; i < 4; i++)
	{
		if (i < 3)
		{
			color = D3DXVECTOR4(
				i == 0 ? 1.f : 0.f,
				i == 1 ? 1.f : 0.f,
				i == 2 ? 1.f : 0.f,
				1.f);
		}
		else
		{
			float alpha = (1.f - flashProgress) * 2.f;
			color = D3DXVECTOR4(1.f, 1.f, 1.f, alpha);
		}

		if (color.w > 1e-6f)
		{
			m_Dev->SetPixelShaderConstantF(0, (const float*)&color, 1);

			pos = srcPos[i] + (dstPos - srcPos[i]) * progress;

			if (i == 3)
				sizeFactor = 1.f + flashProgress*2.f;
			else
				sizeFactor = 1.f;

			VERTEX vb[4] = {
				VERTEX(D3DXVECTOR4(pos.x - m_ImgHalfsize*sizeFactor, pos.y - m_ImgHalfsize*sizeFactor, 0.5f, 1.f), D3DXVECTOR2(0.f, 0.f)),
				VERTEX(D3DXVECTOR4(pos.x + m_ImgHalfsize*sizeFactor, pos.y - m_ImgHalfsize*sizeFactor, 0.5f, 1.f), D3DXVECTOR2(1.f, 0.f)),
				VERTEX(D3DXVECTOR4(pos.x - m_ImgHalfsize*sizeFactor, pos.y + m_ImgHalfsize*sizeFactor, 0.5f, 1.f), D3DXVECTOR2(0.f, 1.f)),
				VERTEX(D3DXVECTOR4(pos.x + m_ImgHalfsize*sizeFactor, pos.y + m_ImgHalfsize*sizeFactor, 0.5f, 1.f), D3DXVECTOR2(1.f, 1.f)),
			};

			m_Dev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vb, sizeof(VERTEX));
		}
	}
}


////////////////////////////////////////////////////////////////////////////////
// class Intro

Intro::Intro(IDirect3DDevice9 *d3dDev, const char *textureFileName, unsigned screenSizeX, unsigned screenSizeY)
: pimpl(new Intro_pimpl(d3dDev, textureFileName, screenSizeX, screenSizeY))
{
}

Intro::~Intro()
{
	delete pimpl;
}

void Intro::Render()
{
	pimpl->Render();
}
